package com.ldy.typeReference;

import org.junit.Test;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 测试TypeReference<br>
 * 版权: Copyright (c) 2011-2018<br>
 * 公司: 活力天汇<br>
 *
 * @author: 刘德咏<br>
 * @date: 2018-04-13<br>
 */
public class TypeReferenceTest {

    @Test
    public void testCommonClass() {
        TypeReferenceTest.Favorites f = new Favorites();
        f.setFavorite(String.class, "Java");
        f.setFavorite(Integer.class, 0xcafebabe);
        String s = f.getFavorite(String.class);
        int i = f.getFavorite(Integer.class);

        System.out.println(String.format("%s:%s", s, i));
    }

    @Test
    public void testReference() throws Exception{
        List<String> l1 = new TypeReference<ArrayList<String>>() {}.newInstance();
        List l2 = new TypeReference<ArrayList>() {}.newInstance();
    }

    public class Favorites {
        private Map<Class<?>, Object> favorites = new HashMap<Class<?>, Object>();

        public <T> void setFavorite(Class<T> klass, T thing) {
            favorites.put(klass, thing);
        }

        public <T> T getFavorite(Class<T> klass) {
            return klass.cast(favorites.get(klass));
        }
    }

    /**
     * References a generic type.
     *
     * @author crazybob@google.com (Bob Lee)
     */
    public abstract class TypeReference<T> {

        private final Type type;
        private volatile Constructor<?> constructor;

        protected TypeReference() {
            Type superclass = getClass().getGenericSuperclass();
            if (superclass instanceof Class) {
                throw new RuntimeException("Missing type parameter.");
            }
            this.type = ((ParameterizedType) superclass).getActualTypeArguments()[0];
        }

        /**
         * Instantiates a new instance of {@code T} using the default, no-arg
         * constructor.
         */
        @SuppressWarnings("unchecked")
        public T newInstance()
                throws NoSuchMethodException, IllegalAccessException,
                InvocationTargetException, InstantiationException {
            if (constructor == null) {
                Class<?> rawType = type instanceof Class<?>
                        ? (Class<?>) type
                        : (Class<?>) ((ParameterizedType) type).getRawType();
                constructor = rawType.getConstructor();
            }
            return (T) constructor.newInstance();
        }

        /**
         * Gets the referenced type.
         */
        public Type getType() {
            return this.type;
        }
    }
}
